/*
 * Copyright @ 苏州瑞云信息技术有限公司 All rights reserved.
 */
package work.base.data.stream;

import lombok.extern.slf4j.Slf4j;
import work.entity.Student;
import work.entity.User;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 *
 * Stream是Java8中处理集合的关键抽象概念,
 * 它可以对集合进行操作, 可以执行非常复杂的查找、过滤和映射数据等操作
 *
 * Stream的特点
 *      1 stream不存储数据, stream对象只能使用一次
 *      2 stream不改变源数据, 会返回一个持有结果的新Stream
 *      3 惰性求值, 流在中间处理过程中, 只是对操作进行了记录, 并不会立即执行
 *
 *
 * Stream的使用
 *      1 Stream接口的 find()方法返回类型是 Optional类型
 *      2 match()方法的参数是个Lambda表达式(Predicate接口), 表示是否匹配
 *      3 Stream接口的 max(), min()求最值方法的参数是 Comparator接口 (接口对应的compare()方法的入参是两个)
 *                  sorted()排序方法的参数同样是Comparator接口
 *                  比较大小的两种方式
 *                      1 Comparator接口的 compare()方法, 参数是两个
 *                          Integer类自身实现了compare()方法, 可以直接使用
 *                      2 Comparable接口的 compareTo()方法, A.compareTo(B)
 *                  Comparator.comparing()底层调用了 Comparable接口的compareTo()方法做比较
 *      4 collect: 接收一个Collector实例, 将流中元素收集成另外一个数据结构
 *                 比如 toSet(), 内部 new HashSet(), 然后add()
 *                 collectingAndThen() 两个参数, 先进行collect, 再进行处理
 *      5 peek和 map
 *          peek接收一个Consumer, 而map接收一个Function。
 *          Consumer是没有返回值的, 它只是对Stream中的元素进行某些操作, 但是操作之后的数据并不返回到Stream中, 所以Stream中的元素还是原来的元素
 *          Function是有返回值的, 这意味着对于 Stream的元素的所有操作都会作为新的结果返回到Stream中
 *
 *
 * 注意
 *      1 求最值和总数量, 有两种写法, 使用 Stream接口的 max() 或者 Collectors类的maxBy()方法, 比较类似
 *      2 list转map, 两种方法, toMap和 groupingBy,
 *          toMap要注意 key冲突的处理措施
 *              前两个参数都是函数式接口 Function, 表示元素自身的写法 item->item和 Function.identity()
 *              第三个参数是用来处理 key冲突的 BinaryOperator接口
 *
 *
 */
@Slf4j
public class StreamTest01 {

    public static void main(String[] args) {

        List<String> mapList = Arrays.asList("a,b,c", "1,2,3");
        // 1 map 用法
        // map是对单个元素进行映射操作
        // 将每个元素转成一个新的元素
        List<String> s1 = mapList.stream().map(s -> s.replaceAll(",", ""))
                .collect(Collectors.toList());
        // map的映射操作支持实体对象和实体对象的映射  包含字符串到实体对象也行
        List<User> userList0 = Stream.of("aaa", "bbb").map(k -> {
            User user = new User();
            user.setName(k);
            return user;
        }).collect(Collectors.toList());
        // abc  123
        s1.forEach(System.out::println);
        // flatMap 一对多映射关系
        // flatMap 把 input Stream 中的层级结构扁平化, 就是将最底层元素抽出来放到一起, 
        // 最终 output 的新 Stream 里面已经没有 List 了, 都是直接的数字.
        // flatMap()方法参数的lambda表达式的返回值类型是Stream
        Stream<String> s3 = mapList.stream().flatMap(s -> {
            //将stream中的单个元素再分割成多个stream对象返回
            String[] split = s.split(",");
            Stream<String> s2 = Arrays.stream(split);
            return s2;
        });
        s3.forEach(System.out::println);


        // 2 sort排序用法
        List<String> list_a = Arrays.asList("yy", "ff", "dd");
        // 2.1 集合内部对象自身实现了Comparable接口的 int compareTo(T o)方法
        // 比如String类
        List<String> list_a11 = list_a.stream().sorted().collect(Collectors.toList());
        Student stu1 = new Student("aa", 10);
        Student stu2 = new Student("bb", 20);
        Student stu3 = new Student("aa", 30);
        Student stu4 = new Student("dd", 40);
        List<Student> studentList = Arrays.asList(stu1, stu2, stu3, stu4);
        // 2.2 自定义排序 内部实现 Comparator接口的 int compare(T o1, T o2)方法
        // 先按姓名升序, 姓名相同则按index升序
        studentList.stream().sorted(
                (o1, o2) -> {
                    if (o1.getName().equals(o2.getName())) {
                        return o1.getIndex() - o2.getIndex();
                    } else {
                        return o1.getName().compareTo(o2.getName());
                    }
                }
        ).forEach(System.out::println);
        // 2.3  通过Comparator接口的comparing()方法
        // 内部通过Comparable接口的compareTo()方法 实现了 int compare(T o1, T o2)方法
        // 参数是一个有返回值方法 一般是比较的对象
        studentList.stream().sorted(Comparator.comparing(Student::getIndex));


        // 3 List根据某个属性去重 两种写法
        // 3.1
        List<User> userList = new ArrayList<>();
        userList.add(new User("111", "SML1", "10", 2));
        userList.add(new User("111", "SML2", "11", 3));
        userList.add(new User("333", "SML3", "12", 1));
        userList = userList.stream().collect(
                Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(User::getId))), ArrayList::new)
        );
        // 3.2 先分组成map 再对每个map的value(是list)进行去重 自定义去重规则
        List<User> newUserList = userList.stream()
                .collect(Collectors.groupingBy(User::getId))
                .values()
                .stream()
                .map(k -> {
                    if (k.size() == 1) {
                        return k.get(0);
                    }
                    Optional<User> optional = k.stream().max(Comparator.comparing(User::getOrder));
                    if (optional.isPresent()) {
                        return optional.get();
                    }
                    log.warn("Can not find last time resolved story: {}", k);
                    return null;
                }).filter(Objects::nonNull).collect(Collectors.toList());

        // 4 filter是符合条件的 返回结果为true才会留下来 false会被削除
        List<User> userList1 = userList.stream().filter(k -> {
            int sum = 0;
            String id = k.getId();
            return true;
        }).collect(Collectors.toList());
        // 空的集合（空集合不是null)也可以使用stream操作 不会报错
        List<String> stringList = new ArrayList<>();
        // 获取空字符串的数量
        String strCount = stringList.stream().filter(string -> string.isEmpty()).findFirst().orElse(null);
        // filter过滤之后 如果没有符合条件的数据 findFirst()返回的是Optional.empty()
        // Optional.empty().get()会报错  使用Optional的orElse()代替
//        Optional.empty().get();
//        String strCount1 = stringList.stream().filter(k -> k.equals("AAA")).findFirst().get();
        System.out.println("空的集合的结果：" + strCount);

        System.out.println(userList);
        System.out.println("AAA");


    }
}
